// Client.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include "conio.h"

#pragma comment(lib, "ws2_32.lib")

#define STRLEN 4096
#define SERVPORT 5400
#define WSVERS MAKEWORD(2, 2)
//#define _WINSOCK_DEPRECATED_NO_WARNINGS

#define USER_NOT_EXISTS "400"
#define INCORRECT_PASSWORD "401"

unsigned WINAPI sendmsg(void * linksock);
unsigned WINAPI recvmsg(void * linksock);

// string buffer
char tempBuf[STRLEN];
char username[20] = "test";
char password[20] = "test";

void testSend(int messageLen) {
	switch (messageLen) {
	case SOCKET_ERROR: printf("send() failed: %d\n", WSAGetLastError());
	case 0: break;
	default: printf("send successfully. \n", messageLen);
	}
	return ;
}

int logIn(SOCKET linksock) {
	SOCKET  lsock = linksock;
	char	recvmsg[STRLEN];
	char	sendmsg[STRLEN];
	struct  sockaddr_in cln_addr;
	int		cln_addr_len;
	int logState;

	// send username
	memset(sendmsg, 0, sizeof(sendmsg));
	fputs("Please input username: ", stdout);
	fgets(sendmsg, sizeof(sendmsg), stdin);
	// strncpy(sendmsg, username, 5);
	// printf("%s\n", sendmsg);
	int messageLen = send(lsock, sendmsg, strlen(sendmsg), 0);
	testSend(messageLen);

	// recv reply
	memset(recvmsg, 0, sizeof(recvmsg));
	int recvlen = recv(lsock, recvmsg, sizeof(recvmsg), 0);

	if (recvlen == 0 || recvlen == SOCKET_ERROR) {
		memset(sendmsg, 0, sizeof(sendmsg));
		sprintf(sendmsg, "log in failed. \n");
		printf("%s", sendmsg);
		return -1;
	}
	else if (recvlen > 0) {
		// user not exists.
		logState = (strstr(recvmsg, USER_NOT_EXISTS) != NULL);
		switch (logState) {
		case 1: printf("user not exists. \n"); return -1; break;
		default: printf("user found. \n"); printf("%s", recvmsg);
		}

		// send password
		memset(sendmsg, 0, sizeof(sendmsg));
		fgets(sendmsg, sizeof(sendmsg), stdin);
		// strncpy(sendmsg, password, 5);
		// printf("%s\n", sendmsg);
		messageLen = send(lsock, sendmsg, strlen(sendmsg), 0);
		testSend(messageLen);

		memset(recvmsg, 0, sizeof(recvmsg));
		recvlen = recv(lsock, recvmsg, sizeof(recvmsg), 0);

		if (recvlen == 0 || recvlen == SOCKET_ERROR) {
			memset(sendmsg, 0, sizeof(sendmsg));
			sprintf(sendmsg, "log in failed. \n");
			printf("%s", sendmsg);
			return -1;
		} else if (recvlen > 0) {
			// wait for reply

			logState = (strstr(recvmsg, INCORRECT_PASSWORD) != NULL);
			switch(logState) {
				case 1: printf("incorrect password. \n"); return -1; break;
				default: printf("correct password. \n");
			}

			printf("%s\n", recvmsg);
			return 1;
		}
	}
}

int main(int argc, char *argv[]) {
	SOCKET cli;
	struct sockaddr_in servSockIn;
	HANDLE	hsendThread, hrecvThread;
	WSADATA wsadata;
	int recvMessageLen;
	if (WSAStartup(WSVERS, &wsadata) != 0) {
		printf("failed to load win socket\n");
		return 1;
	}

	// server address
	memset(&servSockIn, 0, sizeof(servSockIn));
	servSockIn.sin_family = AF_INET;
	servSockIn.sin_addr.s_addr = inet_addr(argv[1]);
	servSockIn.sin_port = htons(SERVPORT);

	// create socket
	cli = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (cli == INVALID_SOCKET) {
		printf("socket() failed: %d\n", WSAGetLastError());
		return 1;
	}

	// connect to server
	printf("connect to %s:%d\n", inet_ntoa(servSockIn.sin_addr), ntohs(servSockIn.sin_port));

	if (connect(cli, (SOCKADDR*)&servSockIn, sizeof(servSockIn)) == INVALID_SOCKET) {
		printf("connection failed: %d\n", WSAGetLastError());
		return 1;
	}
	else {
		printf("connected...\n");
	}

	// log in
	if (logIn(cli) < 0) {
		// log in failed.
		printf("log in failed. This cli will quit.\n");
		return 1;
	}

	// get online user list
	memset(&tempBuf, 0, sizeof(tempBuf));
	recvMessageLen = recv(cli, tempBuf, sizeof(tempBuf), 0);
	if (recvMessageLen > 0) printf("%s", tempBuf);

	// chating
	hsendThread = (HANDLE)_beginthreadex(NULL, 0, &sendmsg, (void *)&cli, 0, NULL);
	hrecvThread = (HANDLE)_beginthreadex(NULL, 0, &recvmsg, (void *)&cli, 0, NULL);

	WaitForSingleObject(hsendThread, INFINITE);
	WaitForSingleObject(hrecvThread, INFINITE);

	// log out
	closesocket(cli);
	WSACleanup();

	return 0;
}

// send-message thread
unsigned WINAPI sendmsg(void * linksock) {
	SOCKET hsock = *((SOCKET*)linksock);
	char wBuf[STRLEN];

	while (true) {
		fputs("Please input a message(or 'quit' to escape): ", stdout);
		fgets(wBuf, STRLEN, stdin);
		sprintf(tempBuf, "message: %s", wBuf);
		int messageLen = send(hsock, tempBuf, strlen(tempBuf), 0);
		testSend(messageLen);

		// talk to somone
		//if (strncmp())

		// log out
		if (strncmp(wBuf, "quit\n", 7) == 0) {
			printf("log out successfully. \n");
			break;
		}
	}

	closesocket(hsock);
	return 0;
}

// recv-message thread
unsigned WINAPI recvmsg(void * linksock) {
	SOCKET  lsock = *((SOCKET*)linksock);
	char	message[STRLEN + 10];
	char	recvreq[STRLEN + 1];
	struct  sockaddr_in cln_addr;
	int		cln_addr_len;

	// memset(&cln_addr, 0, sizeof(cln_addr));
	// cln_addr_len = sizeof(cln_addr);
	// int ret = getsockname(lsock, (sockaddr*)&cln_addr, &cln_addr_len);
	// sprintf(recvreq, "user IP: %s port: %d %s", inet_ntoa(cln_addr.sin_addr), cln_addr.sin_port, message);

	while (true) {
		memset(message, 0, STRLEN);
		int recvlen = recv(lsock, message, STRLEN, 0);
		if (recvlen == SOCKET_ERROR || recvlen == 0) {
			break;
		}
		else if (recvlen > 0) {
			message[recvlen] = '\0';
			// if (strstr(message, recvreq) != NULL)
			// 	break;
			printf("%s", message);
		}
	}

	closesocket(lsock);
	return 0;
}

